package org.xx.bookstore.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.xx.armory.spring5.mvc.OutputCache;
import org.xx.armory.spring5.mvc.OutputTaskLog;
import org.xx.armory.spring5.mvc.ResourceNotFoundException;
import org.xx.bookstore.domain.Book;
import org.xx.bookstore.domain.BookCategory;
import org.xx.bookstore.jdbc.BookDao;

import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;

import static org.xx.armory.spring5.mvc.ModelUtils.JSON_VIEW;
import static org.xx.armory.spring5.mvc.ModelUtils.buildJsonView;
import static org.xx.armory.spring5.mvc.ModelUtils.paramNonNegative;
import static org.xx.armory.spring5.mvc.ModelUtils.paramNotBlank;
import static org.xx.armory.spring5.mvc.ModelUtils.paramNotNull;
import static org.xx.armory.spring5.mvc.ModelUtils.parsePageArgs;

@RequestMapping("/books")
@Controller
public class BookController {
    @Autowired
    private BookDao bookDao;

    @GetMapping
    @OutputCache
    @Transactional(readOnly = true)
    public ModelAndView findAll(
            @RequestParam(required = false) List<BookCategory> categories,
            @RequestParam(required = false) String key,
            @RequestParam(name="min-price", required = false) BigDecimal minPrice,
            @RequestParam(name="max-price", required = false) BigDecimal maxPrice,
            @RequestParam(name="min-pub-date", required = false) Date minPubDate,
            @RequestParam(name="max-pub-date", required = false) Date maxPubDate,
            @RequestParam(required = false) String page
    ) {
        final var pageArgs = parsePageArgs(page);

        final var result = new ModelAndView(JSON_VIEW);

        result.addObject("books", this.bookDao.setPageable(pageArgs).findAll(categories, key, minPubDate, maxPubDate, minPrice, maxPrice));
        result.addObject("count", this.bookDao.countAll(categories, key, minPubDate, maxPubDate, minPrice, maxPrice));

        return result;
    }

    @GetMapping("/{book-id:\\d+}")
    @Transactional(readOnly = true)
    public ModelAndView findById(
            @PathVariable("book-id") long bookId
    ) {
        paramNonNegative(bookId, "bookId");

        if (bookId == 0) {
            return buildJsonView(newBlankBook());
        }

        final var book = this.bookDao.findById(bookId);
        if (book == null) {
            throw new ResourceNotFoundException(bookId);
        }

        return buildJsonView(book);
    }

    private Book newBlankBook() {
        return new Book(0L);
    }

    @PostMapping({"", "{book-id:\\d+}"})
    @OutputTaskLog(category = "books", action="save")
    @Transactional
    public ModelAndView save(
            @PathVariable(value = "book-id",
                          required = false) Long bookId,
            @RequestParam(value = "category",
                          defaultValue = "1") BookCategory category,
            @RequestParam(value = "title") String title,
            @RequestParam(value = "author") String author,
            @RequestParam(value = "pub-date") Date pubDate,
            @RequestParam(value = "price") BigDecimal price,
            @RequestParam(value = "amount",
                          defaultValue = "1") long amount,
            @RequestParam(value = "star",
                          defaultValue = "no") boolean star,
            @RequestParam(value = "cover",
                          defaultValue = "") ByteBuffer cover
    ) {
        if (bookId == null) {
            bookId = 0L;
        }

        paramNonNegative(bookId, "book-id");
        paramNotNull(category, "category");
        paramNotBlank(title, "title");
        paramNotBlank(author, "author");
        paramNotNull(pubDate, "pub-date");

        return buildJsonView(this.bookDao.save(bookId, category, title, author, pubDate, price, amount, star, cover));
    }

    @DeleteMapping("/{book-id:\\d+}")
    @Transactional
    public ModelAndView deleteById(
            @PathVariable(value = "book-id") long bookId
    ) {
        paramNonNegative(bookId, "bookId");

        final var book = this.bookDao.findById(bookId);
        if (this.bookDao.deleteById(bookId) > 0) {
            return buildJsonView(book);
        } else {
            return buildJsonView(null);
        }
    }
}
